﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;

namespace Tools.InfoStorage
{
    public class ConvertObject
    {
        public static string DataTableToXml(DataTable table)
        {
            XmlTextWriter writer = null;
            try
            {
                var stream = new MemoryStream();
                table.WriteXml(stream);
                writer = new XmlTextWriter(stream, Encoding.Default);
                var count = (int)stream.Length;
                var arr = new byte[count];
                stream.Seek(0, SeekOrigin.Begin);
                stream.Read(arr, 0, count);
                var utf = new UTF8Encoding();
                string xmlText = utf.GetString(arr).Trim();
                return string.Format("<?xml version={0}1.0{0} encoding={0}utf-8{0}?>\n{1}", '"', xmlText);
            }
            catch
            {
                return String.Empty;
            }
            finally
            {
                if (writer != null) writer.Close();
            }
        }

        public static KeyValuePair<string, string>[] UrlDataToKeyValuePair(string data)
        {
            var array = data.Split('&').Select(v => v.Split('=')).Where(sc => sc.Length == 2).ToDictionary(sc => sc[0].Trim(), sc => sc[1].Trim());
            var result = (from v in array orderby v.Key select v).ToArray();
            return result;
        }

        public static string UnicodeToString(string str)
        {
            string s = "";
            var sc = str.Split(new char[] { '&', '#', ';' });
            foreach (var item in sc)
            {
                if (item.Equals("")) continue;
                int a;
                int.TryParse(item, out a);
                s += a == 0 ? item : ((char)a).ToString();
            }
            return s;
        }

        #region Object

        public static string ObjectToXml(object t, string rootName = "root", bool isVersion = true, string en = "gbk")
        {
            var xmlText = new StringBuilder();
            if (!rootName.Equals("")) xmlText.AppendFormat("<{0}>\n", rootName);
            PropertyInfo[] properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object value = item.GetValue(t, null);
                xmlText.AppendFormat("\t<{0}>{1}</{0}>\n", name.ToLower(), (value ?? "").ToString());
            }
            if (!rootName.Equals("")) xmlText.AppendFormat("</{0}>", rootName);
            var xmlStr = isVersion ? string.Format("<?xml version={0}1.0{0} encoding={0}{2}{0}?>\n{1}", '"', xmlText, en) : xmlText.ToString();
            return xmlStr;
        }

        public static string ObjectToString(object t)
        {
            var text = new StringBuilder();
            PropertyInfo[] properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object value = item.GetValue(t, null);
                text.AppendFormat("{0}={1}&", name.ToLower(), (value ?? "").ToString().ToLower());
            }
            var data = UrlDataToKeyValuePair(text.ToString());
            text.Clear();
            foreach (var keyValuePair in data)
            {
                text.AppendFormat("{0}{1}", keyValuePair.Key, keyValuePair.Value);
            }
            return text.ToString();
        }

        public static string ObjectToUrlParameter(object t)
        {
            var text = new StringBuilder();
            PropertyInfo[] properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object value = item.GetValue(t, null);
                text.AppendFormat("{0}={1}&", name.ToLower(), (value ?? "").ToString().ToLower());
            }
            return text.ToString();
        }

        public static string ObjectToUrlSign(object t)
        {
            var text = new StringBuilder();
            PropertyInfo[] properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            var array = new Dictionary<string, string>();
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                object value = item.GetValue(t, null);
                array.Add(name, (value ?? "").ToString());
            }

            var keys = (from a in array orderby a.Key select a).ToArray();

            foreach (KeyValuePair<string, string> item in keys)
            {
                string name = item.Key;
                string value = item.Value;
                text.AppendFormat("{0}{1}", name.ToLower(), value.ToLower());
            }
            return text.ToString();
        }

        #endregion

        #region Xml转换Json

        public static string XmlToJSON(XmlDocument xmlDoc)
        {
            var sbJSON = new StringBuilder();
            sbJSON.Append("{ ");
            XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true);
            sbJSON.Append("}");
            return sbJSON.ToString();
        }

        private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
        {
            if (showNodeName)
                sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
            sbJSON.Append("{");
            // Build a sorted list of key-value pairs
            //  where   key is case-sensitive nodeName
            //          value is an ArrayList of string or XmlElement
            //  so that we know whether the nodeName is an array or not.
            var childNodeNames = new SortedList();

            //  Add in all node attributes
            if (node.Attributes != null)
                foreach (XmlAttribute attr in node.Attributes)
                    StoreChildNode(childNodeNames, attr.Name, attr.InnerText);

            //  Add in all nodes
            foreach (XmlNode cnode in node.ChildNodes)
            {
                if (cnode is XmlText)
                    StoreChildNode(childNodeNames, "value", cnode.InnerText);
                else if (cnode is XmlElement)
                    StoreChildNode(childNodeNames, cnode.Name, cnode);
            }

            // Now output all stored info
            foreach (string childname in childNodeNames.Keys)
            {
                var alChild = (ArrayList)childNodeNames[childname];
                if (alChild.Count == 1)
                    OutputNode(childname, alChild[0], sbJSON, true);
                else
                {
                    sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
                    foreach (object Child in alChild)
                        OutputNode(childname, Child, sbJSON, false);
                    sbJSON.Remove(sbJSON.Length - 2, 2);
                    sbJSON.Append(" ], ");
                }
            }
            sbJSON.Remove(sbJSON.Length - 2, 2);
            sbJSON.Append(" }");
        }

        private static void StoreChildNode(SortedList childNodeNames, string nodeName, object nodeValue)
        {
            // Pre-process contraction of XmlElement-s
            if (nodeValue is XmlElement)
            {
                // Convert  <aa></aa> into "aa":null
                //          <aa>xx</aa> into "aa":"xx"
                var cnode = (XmlNode)nodeValue;
                if (cnode.Attributes == null && cnode.Attributes.Count == 0)
                {
                    var children = cnode.ChildNodes;
                    if (children.Count == 0)
                        nodeValue = null;
                    else if (children.Count == 1 && (children[0] is XmlText))
                        nodeValue = children[0].InnerText;
                }
            }
            // Add nodeValue to ArrayList associated with each nodeName
            // If nodeName doesn't exist then add it
            object oValuesAL = childNodeNames[nodeName];
            ArrayList valuesAL;
            if (oValuesAL == null)
            {
                valuesAL = new ArrayList();
                childNodeNames[nodeName] = valuesAL;
            }
            else
                valuesAL = (ArrayList)oValuesAL;
            valuesAL.Add(nodeValue);
        }

        private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName)
        {
            if (alChild == null)
            {
                if (showNodeName)
                    sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
                sbJSON.Append("null");
            }
            else if (alChild is string)
            {
                if (showNodeName)
                    sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
                var sChild = (string)alChild;
                sChild = sChild.Trim();
                sbJSON.Append("\"" + SafeJSON(sChild) + "\"");
            }
            else
                XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName);
            sbJSON.Append(", ");
        }

        private static string SafeJSON(string sIn)
        {
            var sbOut = new StringBuilder(sIn.Length);
            foreach (char ch in sIn)
            {
                if (Char.IsControl(ch) || ch == '\'')
                {
                    var ich = (int)ch;
                    sbOut.Append(@"\u" + ich.ToString("x4"));
                    continue;
                }
                if (ch == '\"' || ch == '\\' || ch == '/')
                {
                    sbOut.Append('\\');
                }
                sbOut.Append(ch);
            }
            return sbOut.ToString();
        }

        #endregion

        #region Byte转换

        /// <summary>
        /// 将字节数组转化为数值
        /// </summary>
        /// <param name="arrByte"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public static int ConvertBytesToInt(byte[] arrByte, int offset)
        {
            return BitConverter.ToInt32(arrByte, offset);
        }

        /// <summary>
        /// 将数值转化为字节数组
        /// </summary>
        /// <param name="value"></param>
        /// <param name="reverse"></param>
        /// <returns></returns>
        public static byte[] ConvertIntToBytes(int value, bool reverse)
        {
            byte[] ret = BitConverter.GetBytes(value);
            if (reverse) Array.Reverse(ret);
            return ret;
        }

        /// <summary>
        /// 将字节数组转化为16进制字符串
        /// </summary>
        /// <param name="arrByte"></param>
        /// <param name="reverse"></param>
        /// <returns></returns>
        public static string ConvertBytesToHex(byte[] arrByte, bool reverse)
        {
            StringBuilder sb = new StringBuilder();
            if (reverse) Array.Reverse(arrByte);
            foreach (byte b in arrByte) sb.AppendFormat("{0:x2}", b);
            return sb.ToString();
        }

        /// <summary>
        /// 将16进制字符串转化为字节数组
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] ConvertHexToBytes(string value)
        {
            int len = value.Length / 2;
            byte[] ret = new byte[len];
            for (int i = 0; i < len; i++) ret[i] = (byte)(Convert.ToInt32(value.Substring(i * 2, 2), 16));
            return ret;
        }

        #endregion
    }
}
